<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2021/2/7
 * Time: 23:55
 */
declare(strict_types=1);

namespace App\Service;
use App\Model\Dao\MchAccountAccessDao;
use App\Model\Dao\MchAccountAccessDetailDao;
use App\Model\Dao\MchAccountDao;
use App\Model\Dao\RateSettleDao;
use App\Model\Dao\TradeOrderDetailDao;
use App\Model\Dao\TradeOrderFrozenDao;
use App\Utils\CustomUtils;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Exception;

/**
 * Class SettleTradeOrderFrozenService
 * @package App\Service
 * 交易订单冻结结算
 */
class SettleTradeOrderFrozenService extends CommonService
{

    /**
     * @Inject()
     * @var TradeOrderFrozenDao
     */
    private $TradeOrderFrozenDao;

    /**
     * @Inject()
     * @var TradeOrderDetailDao
     */
    private $TradeOrderDetailDao;

    /**
     * @Inject()
     * @var MchAccountDao
     */
    private $MchAccountDao;

    /**
     * @Inject()
     * @var MchAccountAccessDao
     */
    private $MchAccountAccessDao;

    /**
     * @Inject()
     * @var MchAccountAccessDetailDao
     */
    private $MchAccountAccessDetailDao;

    /**
     * @Inject()
     * @var RateSettleDao
     */
    private $RateSettleDao;

    public function handle($param) {
        // 校验
        $tradeOrderFrozen = $this->TradeOrderFrozenDao->get($param['frozenId']);
        if(!$tradeOrderFrozen) {
            throw new Exception('冻结单不存在，终止处理', FAILED_CODE);
        }
        if($tradeOrderFrozen['handleStatus'] != $param['handleStatus']) {
            throw new Exception('冻结单处理状态不对应', FAILED_CODE);
        }
        if($tradeOrderFrozen['settleStatus'] == 1) {
            throw new Exception('冻结单已冻结结算完成', FAILED_CODE);
        }
        $tradeOrder = $this->TradeOrderDetailDao->get($tradeOrderFrozen['orderId']);
        if($tradeOrder['isSettle'] != 2) {
            throw new Exception('冻结单原始交易订单未结算', FAILED_CODE);
        }
        $mchAccount = $this->MchAccountDao->get($param['mchAccountId']);
        if(!$mchAccount) {
            throw new Exception('查询账户记录失败', FAILED_CODE);
        }
        $isDetail = $this->MchAccountAccessDetailDao->getWhere([
            "frozenId" => $tradeOrderFrozen['id'],
            "type"     => TYPE_TRADE_ORDER_FROZEN_CASH_OUT,
        ]);

        if ($param['handleStatus'] == 0) {
            if ($isDetail && empty($isDetail['accessId'])) {
                throw new Exception('交易冻结单已冻结结算', FAILED_CODE);
            }
            return $this->handleAdd($param, $tradeOrder, $tradeOrderFrozen, $mchAccount);
        } else {
            // 初审通过不用管
            if ($param['handleStatus'] == 1) {
                return $this->handleFirstConfirm($tradeOrderFrozen);
            }
            if ($isDetail && !empty($isDetail['accessId'])) {
                throw new Exception('交易冻结单请先发起冻结结算', FAILED_CODE);
            }
            return $this->handleConfirm($param, $tradeOrder, $tradeOrderFrozen, $mchAccount);
        }
    }

    private function handleFirstConfirm($tradeOrderFrozen) {
        Db::beginTransaction();
        try {
            $this->TradeOrderFrozenDao->set($tradeOrderFrozen['id'], [
                'settleStatus'  => 1,
            ]);

//            // 交易订单初审通过
//            $this->TradeOrderDetailDao->set($tradeOrderFrozen['orderId'], [
//                'isFrozen'  => 2,
//            ]);
        } catch (\Throwable $e) {
            CustomUtils::xzxDebug($e->getMessage());
            Db::rollBack();
            return CustomUtils::xeServiceReturnData(FAILED_CODE, "交易订单冻结初审结算失败");
        }
        Db::commit();
        return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "交易订单冻结初审通过结算成功");
    }

    /**
     * @param $param
     * @param $tradeOrder
     * @param $tradeOrderFrozen
     * @param $mchAccount
     * @return array
     * @throws Exception
     * 新增冻结记录，出入账
     */
    public function handleAdd($param, $tradeOrder, $tradeOrderFrozen, $mchAccount) {


        // 乐观锁版本号+1
        $updateVersion = $mchAccount['updateVersion'] + 1;
        $transSettleAmount = $tradeOrderFrozen['transSettleAmount'];

        // 交易可提现金额 余额前后
        $beforeTradeCashAmount = $mchAccount['tradeCashAmount'];
        $afterTradeCashAmount  = $mchAccount['tradeCashAmount'] - $transSettleAmount;

        // 交易冻结金额 余额前后
        $beforeTradeFrozenAmount = $mchAccount['tradeFrozenAmount'];
        $afterTradeFrozenAmount = $mchAccount['tradeFrozenAmount'] + $transSettleAmount;

        // 开启事务
        Db::beginTransaction();
        try {
            // step
            // 1.可提现出账
            // 2.可提现入账
            // 3.更新订单结算状态
            // 4.更新账户
            $this->handleCashOutTransAmount($param, $tradeOrderFrozen, $mchAccount, $transSettleAmount, $beforeTradeCashAmount, $afterTradeCashAmount);

            $this->handleFrozenInTransAmount($param, $tradeOrderFrozen, $mchAccount, $transSettleAmount, $beforeTradeFrozenAmount, $afterTradeFrozenAmount);

            $updateTradeOrderFrozen = array(
                'settleStatus' => 1
            );
            $this->TradeOrderFrozenDao->update($param['frozenId'], $updateTradeOrderFrozen);

//            // 交易订单初审待审
//            $this->TradeOrderDetailDao->set($tradeOrderFrozen['orderId'], [
//                'isFrozen'  => 1,
//            ]);

            $updateMchAccount = array(
                "tradeCashAmount"   => $afterTradeCashAmount,
                "tradeFrozenAmount" => $afterTradeFrozenAmount,
                "updateVersion"     => $updateVersion
            );
            $result = $this->MchAccountDao->updateWhere([
                "id" => $param['mchAccountId'],
                "updateVersion" => $mchAccount['updateVersion']
            ], $updateMchAccount);

            // 如果更新账户失败，重试，最多3次
            if (!$result) {
                throw new \Exception("更新账户失败");
            }

        } catch (\Throwable $e) {
            CustomUtils::xzxDebug($e->getMessage());
            Db::rollBack();

            // 失败重试，最多3次
            $this->tryOneMore($param);

            return CustomUtils::xeServiceReturnData(FAILED_CODE, "交易冻结单结算失败");
        }
        Db::commit();

        return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "交易冻结单结算成功");
    }

    /**
     * @param $param
     * @param $tradeOrder
     * @param $tradeOrderFrozen
     * @param $mchAccount
     * @return array
     * @throws Exception
     * 冻结单，审核，出入账
     */
    private function handleConfirm($param, $tradeOrder, $tradeOrderFrozen, $mchAccount) {
        // 乐观锁版本号+1
        $updateVersion = $mchAccount['updateVersion'] + 1;
        $transSettleAmount = $tradeOrderFrozen['transSettleAmount'];

        // 交易可提现金额，解冻金额，余额前后
        $beforeFrozenTradeCashAmount = $mchAccount['tradeCashAmount'];
        $afterFrozenTradeCashAmount  = $mchAccount['tradeCashAmount'] + $transSettleAmount;

        // 交易冻结金额，解冻金额，余额前后
        $beforeFrozenTradeFrozenAmount = $mchAccount['tradeFrozenAmount'];
        $afterFrozenTradeFrozenAmount = $mchAccount['tradeFrozenAmount'] - $transSettleAmount;

        // 确认审核成功，交易可提现金额，出账，余额前后
        $beforeOutFrozenTradeCashAmount = $afterFrozenTradeCashAmount;
        $afterOutFrozenTradeCashAmount  = $afterFrozenTradeCashAmount - $transSettleAmount;

        // 确认审核成功，冻结账户金额，入账，余额前后
        $beforeInFrozenTradeFrozenAmount = $afterFrozenTradeFrozenAmount;
        $afterInFrozenTradeFrozenAmount  = $afterFrozenTradeFrozenAmount + $transSettleAmount;

        // 开启事务
        Db::beginTransaction();
        try {
            // step
            // 1.解冻交易冻结出账
            // 2.解冻交易可提现入账
            // 3.复审成功，交易可提现账户出账
            // 4.复审成功，交易冻结账户入账
            // 6.更新冻结订单结算状态
            // 7.复审成功，才会更新订单冻结金额，冻结结算金额
            // 8.更新账户

            // step 1
            $confirmFrozenOutId = $this->handleConfirmFrozenOutTransAmount($param, $tradeOrderFrozen, $mchAccount, $transSettleAmount, $beforeFrozenTradeFrozenAmount, $afterFrozenTradeFrozenAmount);

            // step 2
            $confirmCashInId = $this->handleConfirmCashInTransAmount($param, $tradeOrderFrozen, $mchAccount, $transSettleAmount, $beforeFrozenTradeCashAmount, $afterFrozenTradeCashAmount);

            if ($param['handleStatus'] == 3) {
                // step 3
                $accessId = $this->handleConfirmCashOutTransAmount($param, $tradeOrderFrozen, $mchAccount, $transSettleAmount, $beforeOutFrozenTradeCashAmount, $afterOutFrozenTradeCashAmount);

                $this->MchAccountAccessDetailDao->update($confirmCashInId, ['accessId' => $accessId]);

                // handleAdd新增时添加的记录，查询
                $cashOutDetail = $this->MchAccountAccessDetailDao->getWhere(['frozenId'  => $tradeOrderFrozen['id'], 'type' => TYPE_TRADE_ORDER_FROZEN_CASH_OUT]);

                $this->MchAccountAccessDetailDao->update($cashOutDetail['id'], ['accessId' => $accessId]);

                // step 4
                $accessId = $this->handleConfirmFrozenInTransAmount($param, $tradeOrderFrozen, $mchAccount, $transSettleAmount, $beforeInFrozenTradeFrozenAmount, $afterInFrozenTradeFrozenAmount);

                $this->MchAccountAccessDetailDao->update($confirmFrozenOutId, ['accessId' => $accessId]);

                // handleAdd新增时添加的记录，查询
                $frozenInDetail = $this->MchAccountAccessDetailDao->getWhere(['frozenId'  => $tradeOrderFrozen['id'], 'type' => TYPE_TRADE_ORDER_FROZEN_FROZEN_IN]);

                $this->MchAccountAccessDetailDao->update($frozenInDetail['id'], ['accessId' => $accessId]);
            }

            // step 5
            $this->TradeOrderFrozenDao->set($tradeOrderFrozen['id'], [
                'settleStatus'  => 1,
                'settleTime'    => time()
            ]);

            // 审核成功
            if ($param['handleStatus'] == 3) {

                // step 7
                $updateMchAccount = array(
                    "tradeCashAmount"   => $afterOutFrozenTradeCashAmount,
                    "tradeFrozenAmount" => $afterInFrozenTradeFrozenAmount,
                    "updateVersion"     => $updateVersion
                );
            } else {
                // step 7
                $updateMchAccount = array(
                    "tradeCashAmount"   => $afterFrozenTradeCashAmount,
                    "tradeFrozenAmount" => $afterFrozenTradeFrozenAmount,
                    "updateVersion"     => $updateVersion
                );
            }
            $result = $this->MchAccountDao->updateWhere([
                "id" => $param['mchAccountId'],
                "updateVersion" => $mchAccount['updateVersion']
            ], $updateMchAccount);

            // 如果更新账户失败，重试，最多3次
            if (!$result) {
                throw new \Exception("更新账户失败");
            }

        } catch (\Throwable $e) {
            CustomUtils::xzxDebug($e->getMessage());
            Db::rollBack();

            // 失败重试，最多3次
            $this->tryOneMore($param);

            return CustomUtils::xeServiceReturnData(FAILED_CODE, "交易冻结单审核结算失败");
        }
        Db::commit();

        return CustomUtils::xeServiceReturnData(SUCCESS_CODE, "交易冻结单审核结算成功");
    }

    /**
     * @param $param
     * @param $tradeOrderFrozen
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * 提交记录，冻结金额，可提现出账
     */
    private function handleCashOutTransAmount($param, $tradeOrderFrozen, $mchAccount, $amount, $beforeAmount, $afterAmount) {

        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $tradeOrderFrozen["mchId"],
            "mchNo"             => $tradeOrderFrozen["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accountType"       => ACCOUNT_TYPE_CASH,
            "inOutType"         => DETAIL_TYPE_OUT,
            "type"              => TYPE_TRADE_ORDER_FROZEN_CASH_OUT,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => -$amount,
            "currency"          => $tradeOrderFrozen['transSettleCurrency'],
            "orderId"           => $tradeOrderFrozen['orderId'],
            "tradeNo"           => $tradeOrderFrozen['tradeNo'],
            "frozenId"          => $tradeOrderFrozen['id'],
            "remark"            => "交易订单冻结冻结可提现账户出账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
    }

    /**
     * @param $param
     * @param $tradeOrderFrozen
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * 提交记录，冻结金额，冻结金额入账
     */
    private function handleFrozenInTransAmount($param, $tradeOrderFrozen, $mchAccount, $amount, $beforeAmount, $afterAmount) {

        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $tradeOrderFrozen["mchId"],
            "mchNo"             => $tradeOrderFrozen["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accountType"       => ACCOUNT_TYPE_CASH_FROZEN,
            "inOutType"         => DETAIL_TYPE_IN,
            "type"              => TYPE_TRADE_ORDER_FROZEN_FROZEN_IN,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => $amount,
            "currency"          => $tradeOrderFrozen['transSettleCurrency'],
            "orderId"           => $tradeOrderFrozen['orderId'],
            "tradeNo"           => $tradeOrderFrozen['tradeNo'],
            "frozenId"          => $tradeOrderFrozen['id'],
            "remark"            => "交易订单冻结冻结冻结账户入账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
    }

    /**
     * @param $param
     * @param $tradeOrderFrozen
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * @return string
     * 审核，冻结金额，可提现入账
     */
    private function handleConfirmCashInTransAmount($param, $tradeOrderFrozen, $mchAccount, $amount, $beforeAmount, $afterAmount) {
        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $tradeOrderFrozen["mchId"],
            "mchNo"             => $tradeOrderFrozen["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accountType"       => ACCOUNT_TYPE_CASH,
            "inOutType"         => DETAIL_TYPE_IN,
            "type"              => TYPE_TRADE_ORDER_FROZEN_CONFIRM_CASH_IN,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => $amount,
            "currency"          => $tradeOrderFrozen['transSettleCurrency'],
            "orderId"           => $tradeOrderFrozen['orderId'],
            "tradeNo"           => $tradeOrderFrozen['tradeNo'],
            "frozenId"          => $tradeOrderFrozen['id'],
            "remark"            => "交易订单冻结冻结可提现账户入账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
        return $mchAccountAccessDetail['id'];
    }

    /**
     * @param $param
     * @param $tradeOrderFrozen
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * @return string
     * 确认，冻结金额，冻结金额出账
     */
    private function handleConfirmFrozenOutTransAmount($param, $tradeOrderFrozen, $mchAccount, $amount, $beforeAmount, $afterAmount) {
        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $tradeOrderFrozen["mchId"],
            "mchNo"             => $tradeOrderFrozen["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accountType"       => ACCOUNT_TYPE_CASH_FROZEN,
            "inOutType"         => DETAIL_TYPE_OUT,
            "type"              => TYPE_TRADE_ORDER_FROZEN_CONFIRM_FROZEN_OUT,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => -$amount,
            "currency"          => $tradeOrderFrozen['transSettleCurrency'],
            "orderId"           => $tradeOrderFrozen['orderId'],
            "tradeNo"           => $tradeOrderFrozen['tradeNo'],
            "frozenId"          => $tradeOrderFrozen['id'],
            "remark"            => "交易订单冻结解冻冻结账户出账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);
        return $mchAccountAccessDetail['id'];
    }

    /**
     * @param $param
     * @param $tradeOrderFrozen
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * @return string
     * 审核，冻结金额，可提现出账，冻结成功
     */
    private function handleConfirmCashOutTransAmount($param, $tradeOrderFrozen, $mchAccount, $amount, $beforeAmount, $afterAmount) {
        // 写主表
        $mchAccountAccess = array(
            "id"           => CustomUtils::createUUID(),
            "mchId"        => $tradeOrderFrozen["mchId"],
            "mchNo"        => $tradeOrderFrozen["mchNo"],
            "mchAccountId" => $param['mchAccountId'],
            "accessType"   => ACCESS_TYPE_TRADE_ORDER_FROZEN,
            "accountType"  => ACCOUNT_TYPE_CASH,
            "orderId"      => $tradeOrderFrozen['orderId'],
            "tradeNo"      => $tradeOrderFrozen['tradeNo'],
            "frozenId"     => $tradeOrderFrozen['id'],
            "amount"       => -$amount,
            "currency"     => $tradeOrderFrozen['transSettleCurrency'],
            "status"       => 1,
            "remark"       => "交易订单冻结",
            "createTime"   => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDao->add($mchAccountAccess);

        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $tradeOrderFrozen["mchId"],
            "mchNo"             => $tradeOrderFrozen["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accessId"          => $mchAccountAccess['id'],
            "accountType"       => ACCOUNT_TYPE_CASH,
            "inOutType"         => DETAIL_TYPE_OUT,
            "type"              => TYPE_TRADE_ORDER_FROZEN_CONFIRM_CASH_OUT,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => -$amount,
            "currency"          => $tradeOrderFrozen['transSettleCurrency'],
            "orderId"           => $tradeOrderFrozen['orderId'],
            "tradeNo"           => $tradeOrderFrozen['tradeNo'],
            "frozenId"          => $tradeOrderFrozen['id'],
            "remark"            => "交易订单冻结",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);

        return $mchAccountAccess['id'];
    }

    /**
     * @param $param
     * @param $tradeOrderFrozen
     * @param $mchAccount
     * @param $amount
     * @param $beforeAmount
     * @param $afterAmount
     * @return string
     * 审核，冻结金额，冻结账户入账，冻结成功
     */
    private function handleConfirmFrozenInTransAmount($param, $tradeOrderFrozen, $mchAccount, $amount, $beforeAmount, $afterAmount) {
        // 写主表
        $mchAccountAccess = array(
            "id"           => CustomUtils::createUUID(),
            "mchId"        => $tradeOrderFrozen["mchId"],
            "mchNo"        => $tradeOrderFrozen["mchNo"],
            "mchAccountId" => $param['mchAccountId'],
            "accessType"   => ACCESS_TYPE_TRADE_ORDER_FROZEN,
            "accountType"  => ACCOUNT_TYPE_CASH_FROZEN,
            "orderId"      => $tradeOrderFrozen['orderId'],
            "tradeNo"      => $tradeOrderFrozen['tradeNo'],
            "frozenId"     => $tradeOrderFrozen['id'],
            "amount"       => $amount,
            "currency"     => $tradeOrderFrozen['transSettleCurrency'],
            "status"       => 1,
            "remark"       => "交易订单冻结",
            "createTime"   => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDao->add($mchAccountAccess);

        // 写明细表
        $mchAccountAccessDetail = array(
            "id"                => CustomUtils::createUUID(),
            "mchId"             => $tradeOrderFrozen["mchId"],
            "mchNo"             => $tradeOrderFrozen["mchNo"],
            "mchAccountId"      => $param['mchAccountId'],
            "accessId"          => $mchAccountAccess['id'],
            "accountType"       => ACCOUNT_TYPE_CASH_FROZEN,
            "inOutType"         => DETAIL_TYPE_IN,
            "type"              => TYPE_TRADE_ORDER_FROZEN_CONFIRM_FROZEN_IN,
            "beforeTotalAmount" => $beforeAmount,
            "afterTotalAmount"  => $afterAmount,
            "amount"            => $amount,
            "currency"          => $tradeOrderFrozen['transSettleCurrency'],
            "orderId"           => $tradeOrderFrozen['orderId'],
            "tradeNo"           => $tradeOrderFrozen['tradeNo'],
            "frozenId"          => $tradeOrderFrozen['id'],
            "remark"            => "交易订单冻结冻结账户入账",
            "createTime"        => CustomUtils::xzxGetMsecTime()
        );
        $this->MchAccountAccessDetailDao->add($mchAccountAccessDetail);

        return $mchAccountAccess['id'];
    }

}